﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using EnvDTE80;

namespace SD007
{
    public class ClassAbstraction
    {
        List<MethodAbstraction> methods = new List<MethodAbstraction>();
        List<CodeVariable> variables = new List<CodeVariable>();
        List<VariableAbstraction> variablesAbstraction = new List<VariableAbstraction>();
        List<CodeProperty> properties = new List<CodeProperty>();
        CodeClass2 classObject;
        int? ndc = null;
        int? np = null;
        Decimal? woc = null;
        int? noap = null;
        int? noam = null;
        int? wmc = null;
        Decimal? tcc = null;
        int? atfd = null;

        public List<VariableAbstraction> InstanceVariables
        {
            get { return variablesAbstraction; }
            set { variablesAbstraction = value; }
        }

        public List<MethodAbstraction> publicMethods
        {
            get
            {
                return methods.Where(m => m.MethodObject.Access  == vsCMAccess.vsCMAccessPublic).ToList();
            }
        }

        protected int NDC
        {
            get
            {
                ndc = ndc ?? CalculateNDC();
                return ndc.GetValueOrDefault(0);
            }
        }

        protected int NP
        {
            get
            {
                np = np ?? CalculateNP();
                return np.GetValueOrDefault(0);
            }
        }

        public int NOPA
        {
            get
            {
                noap = noap ?? CalculateNOAP();
                return noap.GetValueOrDefault(0);
            }
        }

        public int NOAM
        {
            get
            {
                noam = noam ?? CalculateNOAM();
                return noam.GetValueOrDefault(0);
            }
        }

        public Decimal WOC
        {
            get
            {
                woc = woc ?? CalculateWOC();
                return woc.GetValueOrDefault(0M);
            }
        }

        public int WMC
        {
            get
            {
                wmc = wmc ?? CalculateWMC();
                return wmc.GetValueOrDefault(0);
            }
        }

        public Decimal TCC
        {
            get
            {
                tcc = tcc ?? CalculateTCC();
                return tcc.GetValueOrDefault(0M);
            }
        }

        public int ATFD
        {
            get
            {
                atfd = atfd ?? CalculateATFD();
                return atfd.GetValueOrDefault(0);
            }
        }

        public ClassAbstraction(CodeClass2 classObject)
        {
            this.classObject = classObject;
            InitializeComponents();
        }

        private void InitializeComponents()
        {
            List<VariableAbstraction> variablesAbstraction = (from v in this.classObject.Members.OfType<CodeVariable>()
                                               select new VariableAbstraction(v.Name, v.Type.AsString)).ToList();
            List<CodeVariable> variables = (from v in this.classObject.Members.OfType<CodeVariable>()
                                                select v).ToList();
            List<CodeProperty> properties = this.classObject.Members.OfType<CodeProperty>().ToList();
            List<MethodAbstraction> methods = (from a in this.classObject.Members.OfType<CodeFunction2>()
                                               select new MethodAbstraction(a, this.classObject,variablesAbstraction)).ToList();
            this.variablesAbstraction = variablesAbstraction;
            this.variables = variables;
            this.properties = properties;
            this.methods = methods;
        }

        protected int CalculateNDC()
        {
            int NDC = 0;
            for (int i = 0; i < this.methods.Count; i++)
                for (int s = 0; s < i; s++)
                    if (this.methods[i].instanceVariablesAccessed.Intersect(this.methods[s].instanceVariablesAccessed).Count() > 0)
                        ++NDC;
            return NDC;
        }

        protected int CalculateNP()
        {
            return (this.publicMethods.Count * (this.publicMethods.Count - 1)) / 2;
        }

        protected Decimal CalculateWOC()
        {
            int qtdPublicMethods = methods.Where(m => m.MethodObject.Access == vsCMAccess.vsCMAccessPublic).Count();
            int qtdPublicInstanceVariables = this.NOPA;
            int qtdPublicGetSetProperties = this.NOAM;
            int qtdPublicMembers = qtdPublicMethods + qtdPublicInstanceVariables + qtdPublicGetSetProperties;
            return (qtdPublicMembers == 0) ? 0 : (qtdPublicMethods / (qtdPublicMembers * 1.0m));
        }

        protected int CalculateNOAP()
        {
            int qtdPublicInstanceVariables = variables.Where(v => v.Access == vsCMAccess.vsCMAccessPublic).Count();
            return qtdPublicInstanceVariables;
        }

        protected int CalculateNOAM()
        {
            int qtdPublicSetProperties = properties.Where(p => p.Setter != null
                && p.Access == vsCMAccess.vsCMAccessPublic).Count();
            int qtdPublicGetProperties = properties.Where(p => p.Getter != null
                && p.Access == vsCMAccess.vsCMAccessPublic).Count();
            return qtdPublicSetProperties + qtdPublicGetProperties;
        }

        protected int CalculateWMC()
        {
            return this.methods.Sum(m => m.CYCLO);
        }

        protected decimal CalculateTCC()
        {
            return (this.NP > 0) ? this.NDC / (this.NP * 1.0M) : 0M;
        }

        private int CalculateATFD()
        {
            List<VariableAbstraction> classAtfdVars = new List<VariableAbstraction>();
            List<VariableAbstraction> classAtfdProperties = new List<VariableAbstraction>();
            foreach (var method in this.methods)
            {
                classAtfdVars = this.AtfdVarListsUnion(classAtfdVars, method.AtfdVars);
                classAtfdProperties = this.AtfdVarListsUnion(classAtfdProperties, method.AtfdProperties);
            }
            return classAtfdVars.Count + classAtfdProperties.Count;
        }

        private List<VariableAbstraction> AtfdVarListsUnion(List<VariableAbstraction> list1, List<VariableAbstraction> list2)
        {
            VariableAbstraction[] aux = new VariableAbstraction[list1.Count];
            list1.CopyTo(aux);
            List<VariableAbstraction> result = aux.ToList();
            foreach (var item in list2)
            {
                if (result.Where(i => (i.Name == item.Name)
                        && (i.TypeName == item.TypeName)
                        && (i.OwnerType == item.OwnerType)).Count() == 0)
                {
                    result.Add(item);
                }
            }
            return result;
        }
    }
}
